Skip to content

fix: normalize ISO-8601 date strings for Safari compatibility (Closes #642)#677

Merged
ritesh-1918 merged 2 commits into
ritesh-1918:gssocfrom
espcris05-commits:fix/safari-date-parsing-642
May 31, 2026
Merged

fix: normalize ISO-8601 date strings for Safari compatibility (Closes #642)#677
ritesh-1918 merged 2 commits into
ritesh-1918:gssocfrom
espcris05-commits:fix/safari-date-parsing-642

Conversation

@espcris05-commits
Copy link
Copy Markdown

@espcris05-commits espcris05-commits commented May 30, 2026

🎯 Fix: Safari Date Parsing

Closes #642

Problem

Safari fails to parse ISO-8601 timestamps without timezone indicator (e.g., 2026-05-29T12:00:00), showing "Invalid Date" on Ticket Timeline.

Solution

Added normalizeDateString() helper that appends "Z" (UTC) when no timezone is present, ensuring cross-browser compatibility.

Changes

  • Frontend/src/utils/dateUtils.js
    • Added normalizeDateString() helper
    • formatTimelineDate() now uses normalized dates
    • Graceful fallback for invalid/corrupt dates → current timestamp

Testing

  • ✅ Chrome: works
  • ✅ Firefox: works
  • ✅ Safari: now works (previously showed "Invalid Date")
  • ✅ Edge: works

Payment: USDC Stellar GCR377MUJ75YKFLNZKT6XPWNDUIEDZDUHUWY5E2LHOBBSSJDU7MJRZXF

Summary by CodeRabbit

  • Bug Fixes

    • Improved date and time parsing and display reliability across all browsers, with specific fixes for Safari compatibility issues to ensure consistent timestamp formatting throughout the application.
  • Tests

    • Added comprehensive unit test coverage for ticket auto-close functionality, including system settings configuration and error scenarios.
    • Added extensive test suite for duplicate detection to validate accuracy and edge case handling.

Review Change Stack

Zero AI and others added 2 commits May 28, 2026 00:43
- test_duplicate_service.py: 7 tests covering check_duplicate
  * Empty store, custom threshold, default threshold
  * Degraded mode, find duplicates, best match, load on demand
- test_auto_close.py: 12 tests covering AutoCloseService
  * System settings fallback, missing/partial data
  * Ticket close success/error, disabled toggle
  * Full run flow with multiple companies
  * Fatal error handling, singleton, test_query

Closes ritesh-1918#297 and ritesh-1918#279
…itesh-1918#642)

Safari fails to parse date strings in 'YYYY-MM-DDTHH:MM:SS' format
(without timezone indicator). Added normalizeDateString() helper that
appends 'Z' when no timezone is present, ensuring cross-browser compatibility.

Changes:
- Frontend/src/utils/dateUtils.js
  - Added normalizeDateString() helper
  - formatTimelineDate() now uses normalized dates
  - Graceful fallback for invalid/corrupt dates
@vercel
Copy link
Copy Markdown

vercel Bot commented May 30, 2026

@espcris05-commits is attempting to deploy a commit to the ritesh Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 30, 2026

📝 Walkthrough

Walkthrough

This PR introduces three independent changes: a Safari-compatible date normalization fix in the frontend date utilities, a new test suite for AutoCloseService, and a new test suite for DuplicateService. Combined, the changes strengthen client-side date handling and backend service reliability through comprehensive test coverage.

Changes

Safari Date Parsing Fix

Layer / File(s) Summary
Date normalization and formatting
Frontend/src/utils/dateUtils.js
Adds normalizeDateString() to append UTC timezone (Z) or UTC midnight timestamps for Safari compatibility. Updates formatTimelineDate() to normalize before parsing and return 'Invalid Date' for invalid inputs. Changes getTimeZoneAbbr() fallback from 'IST' to 'UTC'. Maintains formatFullTimestamp() composition.

AutoCloseService Test Suite

Layer / File(s) Summary
Test infrastructure and fixtures
backend/tests/test_auto_close.py (lines 1–51)
Sets up module imports, mocks Supabase in sys.modules, and provides pytest fixtures that patch create_client and instantiate AutoCloseService with a mocked client.
System settings retrieval tests
backend/tests/test_auto_close.py (lines 54–143)
Validates get_system_settings() behavior for company-specific lookups, exception-driven fallback to defaults, missing-data scenarios, and partial key handling.
Ticket closure operation tests
backend/tests/test_auto_close.py (lines 146–195)
Tests _close_ticket() success path (verifying status="closed" and auto_closed=True updates) and failure path (error count increment and False return on database exceptions).
Batch processing and lifecycle tests
backend/tests/test_auto_close.py (lines 198–343)
Tests run() method for disabled-service early-exit, expired-ticket closure per company settings, company-level auto-close skipping, fatal error handling with stats return, singleton load() behavior, and mocked ticket query results.

DuplicateService Test Suite

Layer / File(s) Summary
Test infrastructure and fixture
backend/tests/test_duplicate_service.py (lines 1–45)
Sets up module imports with mocked sentence_transformers, and provides a pytest fixture that instantiates DuplicateService with preset internal state for testing.
Duplicate detection logic tests
backend/tests/test_duplicate_service.py (lines 46–201)
Tests check_duplicate() across empty ticket store (no match), custom vs. default threshold behavior, degraded mode (model unavailable), duplicate flagging with similarity scores, best-match selection across multiple tickets, and automatic model loading.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested labels

level:critical, type:testing

Poem

🐰 A safari through dates, now smooth and refined,
With tests standing guard o'er both service and time.
Timestamps once tangled now parse without fright,
And duplicates caught ere they slip from our sight. 🌙

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR addresses requirement 1 by normalizing dates in dateUtils.js with a normalizeDateString helper [#642], and requirement 2 by refactoring formatTimelineDate [#642], but fails to implement requirement 3 (robust fallbacks to current timestamp) and requirement 4 (unit tests). Implement graceful fallbacks for empty/corrupt dates to default to current timestamp and add comprehensive unit tests for dateUtils across different timezones and input formats [#642].
Out of Scope Changes check ⚠️ Warning The PR includes two test files (test_auto_close.py and test_duplicate_service.py) that are unrelated to Safari date parsing or the #642 issue objectives. Remove test_auto_close.py and test_duplicate_service.py from this PR as they are out of scope for the Safari date parsing fix; create separate PRs for these test suites if needed.
Docstring Coverage ⚠️ Warning Docstring coverage is 67.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: normalizing ISO-8601 date strings for Safari compatibility, matching the primary objective from issue #642.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Warning

⚠️ This pull request might be slop. It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
backend/tests/test_duplicate_service.py (2)

21-25: 💤 Low value

Redundant import and an incorrect submodule mock.

Line 21 re-imports MagicMock (already imported on Line 13). Line 25 registers sentence_transformers.util.cos_sim as a fake module, but cos_sim is a function, not a submodule — this entry is never resolved by an import and is dead. Since sentence_transformers is already a MagicMock (Line 22), util.cos_sim is auto-stubbed anyway.

♻️ Proposed cleanup
 # Mock heavy dependencies before any import
-from unittest.mock import MagicMock
 sys.modules['sentence_transformers'] = MagicMock()
 sys.modules['sentence_transformers.SentenceTransformer'] = MagicMock()
 sys.modules['sentence_transformers.util'] = MagicMock()
-sys.modules['sentence_transformers.util.cos_sim'] = MagicMock()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_duplicate_service.py` around lines 21 - 25, Remove the
redundant MagicMock import and the incorrect sys.modules entry for
sentence_transformers.util.cos_sim; since MagicMock is already imported earlier
and sys.modules['sentence_transformers'] = MagicMock() will auto-stub attributes
like util.cos_sim, delete the duplicate "from unittest.mock import MagicMock"
and the line registering "sentence_transformers.util.cos_sim" as a module,
leaving only sys.modules['sentence_transformers'] = MagicMock() (and the
optional sys.modules['sentence_transformers.SentenceTransformer'] if you need a
specific attribute stub) to simplify the test setup.

89-115: 💤 Low value

Use the imported SIMILARITY_THRESHOLD instead of hardcoded values.

SIMILARITY_THRESHOLD is imported on Line 27 but never used; this test instead hardcodes the assumption that the default is 0.70 (Line 108). If the default changes, the comment goes stale and the 0.85/0.50 cases may no longer straddle the boundary, weakening the test. Deriving the values from the constant keeps the test self-validating.

♻️ Example
-        # Default threshold is 0.70
-        with patch.object(svc_module.util, 'cos_sim', return_value=MockCosSim(0.85)):
+        above = SIMILARITY_THRESHOLD + 0.1
+        below = SIMILARITY_THRESHOLD - 0.1
+        with patch.object(svc_module.util, 'cos_sim', return_value=MockCosSim(above)):
             result = service.check_duplicate("Some text")
             assert result["is_duplicate"] is True

-        with patch.object(svc_module.util, 'cos_sim', return_value=MockCosSim(0.50)):
+        with patch.object(svc_module.util, 'cos_sim', return_value=MockCosSim(below)):
             result = service.check_duplicate("Some text")
             assert result["is_duplicate"] is False
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_duplicate_service.py` around lines 89 - 115, The test
test_check_duplicate_uses_default_threshold currently hardcodes a presumed
default (0.70) and uses fixed cos_sim return values; instead import and use the
actual SIMILARITY_THRESHOLD constant (already imported at top) to compute values
just above and just below the threshold so the test remains correct if the
default changes—update the two patch.object calls in the test to return values
derived from SIMILARITY_THRESHOLD (e.g., SIMILARITY_THRESHOLD + delta and
SIMILARITY_THRESHOLD - delta) and assert using service.check_duplicate as
before; reference the test function name and the SIMILARITY_THRESHOLD symbol to
locate and change the assertions and patched return values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/tests/test_auto_close.py`:
- Around line 309-317: The test resets the module-global _instance (in
backend.services.auto_close_service) and never restores it, causing state
leakage; modify the test around the load() assertions to save the original value
(orig = mod._instance) before setting mod._instance = None and restore it after
the assertions (in a finally block or teardown) so the module-global is returned
to its prior state; ensure this restoration happens even if the assertions fail
and keep the existing patch of create_client intact.
- Around line 20-24: The test currently sets sys.modules['supabase'] =
supabase_mock at module import time which pollutes sys.modules for other tests;
change this to a scoped mock so the original module is preserved: either use
unittest.mock.patch.dict(sys.modules, {'supabase': supabase_mock}) around the
import of AutoCloseService inside the test or save original =
sys.modules.get('supabase') before setting and restore it after import (or in
teardown); update the code that creates supabase_mock and the import of
AutoCloseService to perform the mocked insertion and restoration within the
test/fixture scope rather than at module scope.

In `@Frontend/src/utils/dateUtils.js`:
- Line 33: The current guard returns the string 'Invalid Date' when
date.getTime() is NaN, but the issue requires defaulting to the current local
timestamp; update the invalid-date branch (the check using
isNaN(date.getTime())) to create/use a new Date() (e.g., const fallback = new
Date()) and return/format that fallback timestamp instead of the literal
'Invalid Date' so downstream code receives the current local time; ensure you
preserve the existing output format/serialization logic around the original
variable `date`.
- Around line 16-22: The current branch in dateUtils.js incorrectly only checks
for '+' when detecting timezone offsets on dateStr, causing negative offsets
(e.g., "2026-05-29T12:00:00-05:00") to be treated as lacking a TZ and producing
malformed strings; update the condition that tests for timezone characters to
also check for '-' (or better, use a regex that detects an ISO-8601 offset after
the time part) so that the block which appends 'Z' (the logic around
dateStr.includes('T') && !dateStr.includes('Z') && !dateStr.includes('+'))
correctly recognizes both '+' and '-' offsets and skips appending 'Z' when a
negative offset is present.
- Around line 12-26: Add unit tests and test runner configuration to cover edge
cases for normalizeDateString and formatTimelineDate: configure a test runner in
Frontend/package.json (e.g., jest or vitest) and add specs that assert behavior
for date-only strings (should append "T00:00:00Z" treating as UTC), inputs
already containing "Z" or positive offsets like "+hh:mm" (should be returned
unchanged or preserve timezone), negative offsets like "-hh:mm" (should NOT get
a trailing "Z"), passing a Date object to formatTimelineDate (ensure it formats
correctly), and malformed/empty inputs (null, undefined, or "Invalid Date" ->
expected null or handled value). Reference normalizeDateString and
formatTimelineDate when writing tests and assert exact returned strings or null
behavior to ensure all branches in normalizeDateString are exercised.

---

Nitpick comments:
In `@backend/tests/test_duplicate_service.py`:
- Around line 21-25: Remove the redundant MagicMock import and the incorrect
sys.modules entry for sentence_transformers.util.cos_sim; since MagicMock is
already imported earlier and sys.modules['sentence_transformers'] = MagicMock()
will auto-stub attributes like util.cos_sim, delete the duplicate "from
unittest.mock import MagicMock" and the line registering
"sentence_transformers.util.cos_sim" as a module, leaving only
sys.modules['sentence_transformers'] = MagicMock() (and the optional
sys.modules['sentence_transformers.SentenceTransformer'] if you need a specific
attribute stub) to simplify the test setup.
- Around line 89-115: The test test_check_duplicate_uses_default_threshold
currently hardcodes a presumed default (0.70) and uses fixed cos_sim return
values; instead import and use the actual SIMILARITY_THRESHOLD constant (already
imported at top) to compute values just above and just below the threshold so
the test remains correct if the default changes—update the two patch.object
calls in the test to return values derived from SIMILARITY_THRESHOLD (e.g.,
SIMILARITY_THRESHOLD + delta and SIMILARITY_THRESHOLD - delta) and assert using
service.check_duplicate as before; reference the test function name and the
SIMILARITY_THRESHOLD symbol to locate and change the assertions and patched
return values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 89172806-8c77-4b39-ae6b-a734e9b350ef

📥 Commits

Reviewing files that changed from the base of the PR and between da8faf2 and cdc1236.

📒 Files selected for processing (3)
  • Frontend/src/utils/dateUtils.js
  • backend/tests/test_auto_close.py
  • backend/tests/test_duplicate_service.py

Comment on lines +20 to +24
# Mock supabase before import
supabase_mock = MagicMock()
sys.modules['supabase'] = supabase_mock

from backend.services.auto_close_service import AutoCloseService
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid global sys.modules pollution during import mocking.

sys.modules['supabase'] is overridden at module scope and never restored, which can affect unrelated tests in the same run.

Suggested fix
+import importlib
+from unittest.mock import patch
+
 # Mock supabase before import
-supabase_mock = MagicMock()
-sys.modules['supabase'] = supabase_mock
-
-from backend.services.auto_close_service import AutoCloseService
+with patch.dict(sys.modules, {"supabase": MagicMock()}):
+    auto_close_module = importlib.import_module("backend.services.auto_close_service")
+AutoCloseService = auto_close_module.AutoCloseService
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_auto_close.py` around lines 20 - 24, The test currently
sets sys.modules['supabase'] = supabase_mock at module import time which
pollutes sys.modules for other tests; change this to a scoped mock so the
original module is preserved: either use unittest.mock.patch.dict(sys.modules,
{'supabase': supabase_mock}) around the import of AutoCloseService inside the
test or save original = sys.modules.get('supabase') before setting and restore
it after import (or in teardown); update the code that creates supabase_mock and
the import of AutoCloseService to perform the mocked insertion and restoration
within the test/fixture scope rather than at module scope.

Comment on lines +309 to +317
# Reset singleton
import backend.services.auto_close_service as mod
mod._instance = None

with patch('backend.services.auto_close_service.create_client', return_value=MagicMock()):
instance1 = load()
instance2 = load()
assert instance1 is instance2

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Restore singleton state after the test to keep test isolation.

Line 311 mutates module-global _instance and leaves it changed after the test, which can create order-dependent failures.

Suggested fix
         import backend.services.auto_close_service as mod
-        mod._instance = None
+        previous_instance = mod._instance
+        mod._instance = None
 
-        with patch('backend.services.auto_close_service.create_client', return_value=MagicMock()):
-            instance1 = load()
-            instance2 = load()
-            assert instance1 is instance2
+        try:
+            with patch('backend.services.auto_close_service.create_client', return_value=MagicMock()):
+                instance1 = load()
+                instance2 = load()
+                assert instance1 is instance2
+        finally:
+            mod._instance = previous_instance
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Reset singleton
import backend.services.auto_close_service as mod
mod._instance = None
with patch('backend.services.auto_close_service.create_client', return_value=MagicMock()):
instance1 = load()
instance2 = load()
assert instance1 is instance2
# Reset singleton
import backend.services.auto_close_service as mod
previous_instance = mod._instance
mod._instance = None
try:
with patch('backend.services.auto_close_service.create_client', return_value=MagicMock()):
instance1 = load()
instance2 = load()
assert instance1 is instance2
finally:
mod._instance = previous_instance
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_auto_close.py` around lines 309 - 317, The test resets the
module-global _instance (in backend.services.auto_close_service) and never
restores it, causing state leakage; modify the test around the load() assertions
to save the original value (orig = mod._instance) before setting mod._instance =
None and restore it after the assertions (in a finally block or teardown) so the
module-global is returned to its prior state; ensure this restoration happens
even if the assertions fail and keep the existing patch of create_client intact.

Comment on lines +12 to +26
const normalizeDateString = (dateStr) => {
if (!dateStr) return null;

// If it already has timezone info, return as-is
if (dateStr.includes('Z') || dateStr.includes('+') || dateStr.includes('T')) {
// Replace "YYYY-MM-DDTHH:MM:SS" with "YYYY-MM-DDTHH:MM:SSZ" if no TZ
if (dateStr.includes('T') && !dateStr.includes('Z') && !dateStr.includes('+')) {
return dateStr + 'Z';
}
return dateStr;
}

// Raw date without time - assume UTC
return dateStr + 'T00:00:00Z';
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for existing dateUtils tests
fd -i 'dateUtils' --extension test.js --extension spec.js
rg -l 'normalizeDateString|formatTimelineDate' --glob '*test*' --glob '*spec*'

Repository: ritesh-1918/HELPDESK.AI

Length of output: 49


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate dateUtils implementation
echo "== dateUtils.js =="
fd -a "dateUtils.js" . || true
fd -a "dateUtils" Frontend . || true

# Find any test/spec files in the repo
echo "== test/spec files =="
fd -t f --hidden --exclude-dir node_modules \
  -g '*.{test,spec}.{js,jsx,ts,tsx}' . \
  | head -n 200

# Search for test files referencing dateUtils / specific functions
echo "== test files referencing dateUtils/normalizeDateString/formatTimelineDate =="
rg -l --hidden --no-ignore-vcs \
  "dateUtils|normalizeDateString|formatTimelineDate" \
  --glob '*test*' --glob '*spec*' . || true

# Search for any imports of dateUtils from test files
echo "== test files importing Frontend/src/utils/dateUtils =="
rg -l --hidden --no-ignore-vcs \
  "from ['\"].*dateUtils['\"]|require\\(['\"].*dateUtils['\"]\\)" \
  --glob '*test*' --glob '*spec*' . || true

# Search for normalizeDateString usage in non-test code (to see if tests might be elsewhere)
echo "== normalizeDateString usage (non-test) =="
rg -n --hidden --no-ignore-vcs \
  "normalizeDateString\\(" \
  --glob '!**/*test*' --glob '!**/*spec*' . | head -n 50 || true

Repository: ritesh-1918/HELPDESK.AI

Length of output: 513


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== dateUtils.js =="
fd -a "dateUtils.js" . || true

echo "== __tests__ / test directories =="
fd -t d --hidden --exclude node_modules \
  -g '*__tests__*' -g '*tests*' -g '*test*' . | head -n 200 || true

echo "== test/spec files (common extensions) =="
fd -t f --hidden --exclude node_modules \
  -g '*.{test,spec}.{js,jsx,ts,tsx}' . \
  | head -n 200 || true

echo "== test/spec files referencing dateUtils/normalizeDateString/formatTimelineDate =="
rg -l --hidden --no-ignore-vcs \
  "dateUtils|normalizeDateString|formatTimelineDate" \
  --glob '*test*' --glob '*spec*' . || true

echo "== imports of Frontend/src/utils/dateUtils from test/spec files =="
rg -l --hidden --no-ignore-vcs \
  "(from ['\"].*dateUtils['\"]|require\\(['\"].*dateUtils['\"]\\))" \
  --glob '*test*' --glob '*spec*' . || true

echo "== any occurrences of normalizeDateString outside test/spec =="
rg -n --hidden --no-ignore-vcs \
  "normalizeDateString\\(" \
  --glob '!**/*test*' --glob '!**/*spec*' . \
  | head -n 50 || true

Repository: ritesh-1918/HELPDESK.AI

Length of output: 640


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== dateUtils implementation =="
fd -a "dateUtils.js" .

echo "== directories that look like tests =="
# Use name globbing via filename patterns (not multiple -g in fd)
find . -type d \( -name "__tests__" -o -name "tests" -o -name "test" -o -name "__test__" -o -name "spec" \) -not -path "*/node_modules/*" | head -n 200 || true

echo "== common test framework config files =="
fd -t f --hidden --exclude node_modules \
  -g '*jest*.{js,ts,mjs,cjs}' \
  -g '*vitest*.{js,ts,mjs,cjs}' \
  -g '*mocha*.{js,ts,mjs,cjs}' \
  -g '*ava*.{js,ts,mjs,cjs}' \
  -g '*enzyme*.{js,ts,mjs,cjs}' \
  -g '*karma*.{js,ts,mjs,cjs}' . \
  | head -n 200 || true

echo "== any test/spec files anywhere (broad) =="
find . -type f -not -path "*/node_modules/*" \
  \( -name "*test*.js" -o -name "*test*.ts" -o -name "*test*.tsx" -o -name "*test*.jsx" \
     -o -name "*spec*.js" -o -name "*spec*.ts" -o -name "*spec*.tsx" -o -name "*spec*.jsx" \
     -o -name "*.test.js" -o -name "*.test.ts" -o -name "*.spec.js" -o -name "*.spec.ts" \) \
  | head -n 200 || true

echo "== files referencing describe()/it()/test() =="
rg -l --hidden --no-ignore-vcs \
  "(^|\\W)(describe|it|test)\\s*\\(" \
  --glob '*.{js,jsx,ts,tsx,mjs,cjs}' . \
  --glob '!*node_modules/*' \
  | head -n 200 || true

echo "== any occurrences of normalizeDateString in non-implementation files =="
rg -n --hidden --no-ignore-vcs \
  "normalizeDateString\\(" \
  --glob '!**/dateUtils.js' \
  --glob '!**/*test*' \
  --glob '!**/*spec*' . || true

Repository: ritesh-1918/HELPDESK.AI

Length of output: 1001


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== dateUtils.js exports/usages =="
sed -n '1,120p' Frontend/src/utils/dateUtils.js

echo "== any references to dateUtils.js in repo (esp. tests) =="
rg -l --hidden --no-ignore-vcs \
  "Frontend/src/utils/dateUtils\\.js|\\bdateUtils\\b" \
  --glob '*test*' --glob '*spec*' --glob 'backend/tests/**' --glob 'scratch/**' . \
  || true

echo "== any references to normalizeDateString / formatTimelineDate anywhere =="
rg -n --hidden --no-ignore-vcs \
  "normalizeDateString\\b|formatTimelineDate\\b" \
  . || true

Repository: ritesh-1918/HELPDESK.AI

Length of output: 4042


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== backend/tests (top 200 files) =="
find backend/tests -type f -maxdepth 5 -print | head -n 200 || true

echo "== dateUtils usage inside backend/tests =="
rg -n --hidden --no-ignore-vcs \
  "dateUtils\\.js|normalizeDateString\\b|formatTimelineDate\\b|getTimeZoneAbbr\\b" \
  backend/tests || true

echo "== Frontend package.json (test scripts/config) =="
find Frontend -maxdepth 2 -name package.json -print -o -name pnpm-lock.yaml -print -o -name yarn.lock -print | head -n 50

if [ -f Frontend/package.json ]; then
  rg -n "test|jest|vitest|mocha|ava" Frontend/package.json || true
fi

echo "== repo-wide test framework config files =="
find . -maxdepth 4 -type f \
  \( -name "jest.config.*" -o -name "vitest.config.*" -o -name "mocha.config.*" -o -name ".mocharc.*" \
     -o -name "karma.conf.*" -o -name "ava.config.*" \) \
  ! -path "*/node_modules/*" | head -n 200

echo "== scratch/test_*.js references to dateUtils =="
rg -n --hidden --no-ignore-vcs \
  "dateUtils\\.js|normalizeDateString\\b|formatTimelineDate\\b|getTimeZoneAbbr\\b" \
  scratch/test_*.js 2>/dev/null || true

Repository: ritesh-1918/HELPDESK.AI

Length of output: 427


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Frontend/package.json (scripts/deps excerpts) =="
cat -n Frontend/package.json | sed -n '1,220p' | sed -n '1,120p'
echo "----"
cat -n Frontend/package.json | sed -n '120,240p' | sed -n '1,120p'
echo "---- scripts search ----"
rg -n "scripts|jest|vitest|mocha|test" Frontend/package.json || true
echo "---- dependencies search ----"
rg -n "jest|vitest|mocha" Frontend/package.json || true

echo "== Frontend test files (search) =="
find Frontend -type d \
  \( -name "__tests__" -o -name "tests" -o -name "test" -o -name "spec" \) \
  -not -path "*/node_modules/*" | head -n 200 || true

echo "== Frontend test/spec files (common patterns) =="
find Frontend -type f -not -path "*/node_modules/*" \
  \( -name "*test*.js" -o -name "*test*.ts" -o -name "*spec*.js" -o -name "*spec*.ts" \) | head -n 200 || true

echo "== repo-wide test framework configs =="
find . -type f -not -path "*/node_modules/*" \
  \( -name "jest.config.*" -o -name "vitest.config.*" -o -name "mocha.config.*" \
     -o -name ".mocharc.*" -o -name "karma.conf.*" -o -name "ava.config.*" \) | head -n 200 || true

echo "== repo-wide jest/vitest/mocha keywords =="
rg -n --hidden --no-ignore-vcs \
  "jest|vitest|mocha" . | head -n 200 || true

Repository: ritesh-1918/HELPDESK.AI

Length of output: 13141


Add unit tests for Frontend/src/utils/dateUtils.js edge cases (timezone/date inputs)

  • No Frontend unit tests/spec coverage found for formatTimelineDate / normalizeDateString, and Frontend/package.json has no test runner configured.
  • Add tests for: date-only strings (UTC behavior), strings with Z and with positive offsets (+hh:mm), negative offset strings (-hh:mm, should not get a trailing Z), passing a Date object (currently used via formatTimelineDate(new Date())), and malformed/empty inputs (null / Invalid Date).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/utils/dateUtils.js` around lines 12 - 26, Add unit tests and
test runner configuration to cover edge cases for normalizeDateString and
formatTimelineDate: configure a test runner in Frontend/package.json (e.g., jest
or vitest) and add specs that assert behavior for date-only strings (should
append "T00:00:00Z" treating as UTC), inputs already containing "Z" or positive
offsets like "+hh:mm" (should be returned unchanged or preserve timezone),
negative offsets like "-hh:mm" (should NOT get a trailing "Z"), passing a Date
object to formatTimelineDate (ensure it formats correctly), and malformed/empty
inputs (null, undefined, or "Invalid Date" -> expected null or handled value).
Reference normalizeDateString and formatTimelineDate when writing tests and
assert exact returned strings or null behavior to ensure all branches in
normalizeDateString are exercised.

Comment on lines +16 to +22
if (dateStr.includes('Z') || dateStr.includes('+') || dateStr.includes('T')) {
// Replace "YYYY-MM-DDTHH:MM:SS" with "YYYY-MM-DDTHH:MM:SSZ" if no TZ
if (dateStr.includes('T') && !dateStr.includes('Z') && !dateStr.includes('+')) {
return dateStr + 'Z';
}
return dateStr;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing check for negative timezone offsets corrupts dates.

The condition checks for + but not - in the timestamp portion. ISO-8601 offsets can be negative (e.g., 2026-05-29T12:00:00-05:00 for US Eastern). This input would incorrectly match line 18 and produce 2026-05-29T12:00:00-05:00Z, a malformed string.

🐛 Proposed fix to detect negative offsets
 const normalizeDateString = (dateStr) => {
   if (!dateStr) return null;
+  
+  // Convert Date objects to ISO string
+  if (dateStr instanceof Date) {
+    return dateStr.toISOString();
+  }

-  // If it already has timezone info, return as-is
-  if (dateStr.includes('Z') || dateStr.includes('+') || dateStr.includes('T')) {
-    // Replace "YYYY-MM-DDTHH:MM:SS" with "YYYY-MM-DDTHH:MM:SSZ" if no TZ
-    if (dateStr.includes('T') && !dateStr.includes('Z') && !dateStr.includes('+')) {
-      return dateStr + 'Z';
-    }
-    return dateStr;
+  // Check if string already has timezone info (Z, +HH:MM, or -HH:MM after T)
+  if (dateStr.includes('Z')) return dateStr;
+  
+  if (dateStr.includes('T')) {
+    const timePart = dateStr.split('T')[1] || '';
+    // Check for offset in time portion (not the date hyphens)
+    if (timePart.includes('+') || timePart.includes('-')) {
+      return dateStr;
+    }
+    // No timezone, append Z
+    return dateStr + 'Z';
   }

   // Raw date without time - assume UTC
   return dateStr + 'T00:00:00Z';
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/utils/dateUtils.js` around lines 16 - 22, The current branch in
dateUtils.js incorrectly only checks for '+' when detecting timezone offsets on
dateStr, causing negative offsets (e.g., "2026-05-29T12:00:00-05:00") to be
treated as lacking a TZ and producing malformed strings; update the condition
that tests for timezone characters to also check for '-' (or better, use a regex
that detects an ISO-8601 offset after the time part) so that the block which
appends 'Z' (the logic around dateStr.includes('T') && !dateStr.includes('Z') &&
!dateStr.includes('+')) correctly recognizes both '+' and '-' offsets and skips
appending 'Z' when a negative offset is present.

hour12: true
});
const date = new Date(normalized);
if (isNaN(date.getTime())) return 'Invalid Date';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fallback behavior differs from stated objective.

The linked issue #642 specifies that corrupt dates should "default to the current local timestamp." The implementation returns the string 'Invalid Date' instead. If the current behavior is intentional (arguably more transparent to users), consider updating the issue/PR description to reflect the design choice.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/utils/dateUtils.js` at line 33, The current guard returns the
string 'Invalid Date' when date.getTime() is NaN, but the issue requires
defaulting to the current local timestamp; update the invalid-date branch (the
check using isNaN(date.getTime())) to create/use a new Date() (e.g., const
fallback = new Date()) and return/format that fallback timestamp instead of the
literal 'Invalid Date' so downstream code receives the current local time;
ensure you preserve the existing output format/serialization logic around the
original variable `date`.

@ritesh-1918 ritesh-1918 changed the base branch from main to gssoc May 31, 2026 06:00
@ritesh-1918 ritesh-1918 added gssoc GirlScript Summer of Code gssoc:approved GSSoC Approved PR level:advanced Advanced level difficulty quality:exceptional Exceptional code quality type:bug Bug fix labels May 31, 2026
@ritesh-1918
Copy link
Copy Markdown
Owner

Hi @espcris05-commits! Thanks for the contribution. I have triaged your PR and set it to merge into the gssoc branch.

⚠️ MANDATORY GSSOC ONBOARDING STEPS:
Before your PR points are finalized on the leaderboard, you MUST complete these required steps:

  1. Star this repository: https://github.com/ritesh-1918/HELPDESK.AI (Mandatory)
  2. 👤 Follow the Project Admin: https://github.com/ritesh-1918 (Mandatory)
  3. 💼 Connect on LinkedIn: https://www.linkedin.com/in/ritesh1908/ (Mandatory)

Welcome to the HELPDESK.AI developer family! 🚀💻

@ritesh-1918 ritesh-1918 merged commit 970e294 into ritesh-1918:gssoc May 31, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved GSSoC Approved PR gssoc GirlScript Summer of Code level:advanced Advanced level difficulty quality:exceptional Exceptional code quality type:bug Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BOUNTY] [level:intermediate] Fix Ticket Timeline Date Parsing Discrepancies on Older Safari Web Browsers

2 participants